const fs = require("fs");
const path = require("path");
const { merge } = require("./merge2ImageByPageCurl.js");
const { loadConfig } = require("../../src/utils/config");
const { getAudioDuration, FFMPEG_PATH } = require("../../src/utils/ffmpeg.js");
const { mergeVideosOrderedByList, mergeAudioVideo, mergeAssVideo, addBackgroundImage } = require("../../src/utils/videoProcessor");
const { generateASSStylesSingleLine } = require("../../src/utils/assStyles.js");
const { translate } = require("../../src/utils/translate");
const { cnText2MultiLines } = require("../../src/utils/cnText2MultiLines");
const { generateWordASSFromFile } = require("../../src/utils/wordAssGenerator");
const { drawSvg2BackgroundImage } = require("./drawBackground.js");

const { execSync } = require("child_process");
const config = loadConfig();

const booksDir = path.join(__dirname, "books");
const backgroundImage = path.join(__dirname, "bg.jpeg");
const tempDir = path.join(booksDir, "../", "temp");
/**
 * 视频两两合并的时候，第二个视频停留的秒数
 */
const SECOND_FRAME_STOP = 2;

/**
 * 获取 book 目录
 * @param {String} bookGroup book 组
 * @param {String} bookID book ID
 * @returns
 */
function getBookDir(bookGroup, bookID) {
  return path.join(booksDir, bookGroup, bookID.toString());
}
// 获取当前目录books下指定组的指定书籍目录下的文件夹和文件的绝对路径列表
/**
 * 获取指定书籍目录下的文件夹和文件的绝对路径列表
 * - 排除temp目录，json文件
 * @param {String} bookGroup book 组
 * @param {String} bookID book ID
 * @returns
 */
function getBookDirFiles(bookGroup, bookID) {
  const bookDir = getBookDir(bookGroup, bookID);
  const arr = fs.readdirSync(bookDir).filter(file => {
    return file.indexOf(".") === -1;
  });
  for (let i = 0; i < arr.length; i++) {
    // 重命名文件夹,如果文件夹名称包含数字，则重命名为开头以0补齐
    const item = arr[i];
    if (item.length === 1) {
      const newName = `0${item}`;
      fs.renameSync(path.join(bookDir, item), path.join(bookDir, newName));
    }
  }
  return fs.readdirSync(bookDir).filter(file => {
    return file.indexOf(".") === -1;
  });
}

/**
 * 调整图片大小以适应背景图片
 * @param {string} imageFile - 原始图片路径
 * @returns {string} 新图片路径
 */
function resizeImage(imageFile) {
  // 获取背景图片信息
  const [bgWidth] = [1080];

  // 计算新宽度为背景图片宽度的90%
  const newWidth = Math.round(bgWidth * 0.9);

  // 创建输出文件路径
  const outputFile = path.join(tempDir, `resized_${path.basename(imageFile)}`);

  // 缩放图片保持宽高比
  const resizeCommand = `"${FFMPEG_PATH}" -y -loglevel quiet -i "${imageFile}" -vf "scale=${newWidth}:-1" "${outputFile}"`;
  execSync(resizeCommand);

  return outputFile;
}

// 根据若干文件夹和文件生成书籍
async function createBook(bookGroup, bookID) {
  const bookDir = getBookDir(bookGroup, bookID);

  const tempVideos = [];
  // 获取bookDir目录下的title.txt内容
  const bookTitle = fs.readFileSync(path.join(bookDir, "title.txt"), "utf-8");
  const bookDirFiles = getBookDirFiles(bookGroup, bookID);
  for (let i = 1; i < bookDirFiles.length - 1; i++) {
    const bookDirFile = bookDirFiles[i];
    if (bookDirFile.indexOf(".") > -1) {
      continue;
    }
    console.log(` ■■■■■■■■■■■■■■■■■■■■■■■`);
    console.log(` Processing ${bookDirFile}`);
    const bookDirFileStat = fs.statSync(path.join(bookDir, bookDirFile));
    if (!bookDirFileStat.isDirectory()) {
      continue;
    }
    // 获取文件夹下的audio.mp3文件
    const audioFile = path.join(bookDir, bookDirFile, "audio.mp3");
    console.log(` 1. get audio file: ${audioFile}`);
    // 获取文件夹下的image.jpg
    let imageFile = path.join(bookDir, bookDirFile, "image.jpg");
    // 如果图片不存在，则采用前一个文件夹下的image.jpg
    if (!fs.existsSync(imageFile)) {
      imageFile = path.join(bookDir, bookDirFiles[i - 1], "image.jpg");
    }
    console.log(` 2. get image file: ${imageFile}`);
    // 获取文件夹下的text.txt的内容
    let text = fs.readFileSync(path.join(bookDir, bookDirFile, "text.txt"), "utf-8");
    if (bookDirFile === "01") {
      text = bookTitle;
    }
    const textCnFile = path.join(bookDir, bookDirFile, "text_cn.txt");
    let text_cn = "";
    if (fs.existsSync(textCnFile)) {
      text_cn = fs.readFileSync(textCnFile, "utf-8");
    } else {
      let cnText = await translate(text);
      cnText = cnText2MultiLines(cnText);
      text_cn = cnText;
      fs.writeFileSync(textCnFile, cnText);
      console.warn("  ❌️创建了中文台词文本文件，请进一步确认是否合理");
      continue;
    }
    console.log(` 3. get text: ${text}`);
    console.log(` 3. get text: ${text_cn}`);

    // 获取audioFile的时长
    const audioDuration = getAudioDuration(audioFile);
    console.log(` 4. get audio duration: ${audioDuration}`);
    if (i === bookDirFiles.length - 2) {
      console.log(` Processing last page`);
      // 将imageFile宽高等比缩放为backgroundImage宽度的90%
      const newImageFile = resizeImage(imageFile);
      // 将newImageFile叠放在backgroundImage的垂直居中，水平居中
      const outputImageFile = path.join(tempDir, `last-a-combined_${path.basename(imageFile)}`);
      const overlayCommand = `"${FFMPEG_PATH}" -y -loglevel quiet -i "${backgroundImage}" -i "${newImageFile}" -filter_complex "overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2" "${outputImageFile}"`;
      execSync(overlayCommand);

      // 将outputImageFile转为时长为audioDuration的视频
      const outputVideoFile = path.join(tempDir, `${bookID}_last-b_last_page.mp4`);
      const imageToVideoCommand = `"${FFMPEG_PATH}" -y -loglevel quiet -loop 1 -i "${outputImageFile}" -c:v libx264 -t ${audioDuration + 1} -pix_fmt yuv420p "${outputVideoFile}"`;
      execSync(imageToVideoCommand);
      // 合并视频和音频
      const mergedVideoFile = path.join(tempDir, `${bookID}_last-b_with_audio.mp4`);
      console.log(` last. merge video and audio: ${mergedVideoFile}`);
      const targetDuration = getVideoDuration(outputVideoFile);
      mergeAudioVideo(outputVideoFile, audioFile, mergedVideoFile, targetDuration);
      const assFile = path.join(tempDir, `${bookID}_last-c.ass`);
      console.log(` last. generate ass file: ${assFile}`);
      // 生成ass文件
      generateASSFile(text, text_cn, audioDuration, assFile);
      const videoWithAss = path.join(tempDir, `${bookID}_last-d_page_with_ass.mp4`);
      console.log(` last. merge ass file: ${assFile}`);
      // 合并ass文件和视频
      mergeAssVideo(mergedVideoFile, assFile, videoWithAss);

      // 生成单词字幕
      const wordAssFilePath = path.join(tempDir, `${bookID}_last-e_word.ass`);
      process.env.wordType = "nomoveeffect";
      await generateWordASSFromFile(assFile, wordAssFilePath, config.projectSettings.wordStyles);
      process.env.wordType = undefined;
      // 合并视频和单词字幕
      const outputVideoFileWithWordAss = path.join(tempDir, `${bookID}_last-f_with_word_ass.mp4`);
      mergeAssVideo(videoWithAss, wordAssFilePath, outputVideoFileWithWordAss);

      tempVideos.push(outputVideoFileWithWordAss);
      continue;
    }
    // 获取下一个文件夹下audioFile的时长
    // const nextAudioDuration = getAudioDuration(path.join(bookDirFiles[i + 1], "audio.mp3"));
    const options = {
      curlDuration: 0.5,
      frame1Duration: audioDuration,
      frame2Duration: SECOND_FRAME_STOP,
    };
    // 合并前一个文件夹下的图片和当前文件夹下的图片
    const nextImageFile = path.join(bookDir, bookDirFiles[i + 1], "image.jpg");
    console.log(` 5. get prev image file: ${nextImageFile}`);
    let outputVideoFileTemp1 = path.join(tempDir, `${bookID}_${i}_${i + 1}-a_no_audio.mp4`);
    console.log(` 6. merge image file: ${nextImageFile} and ${imageFile}`);
    await merge(imageFile, nextImageFile, outputVideoFileTemp1, options);

    // 将backgroundImage转化为和outputVideoFileTemp1相同时长的视频
    let outputVideoFileTemp1WithBackground = path.join(tempDir, `${bookID}_${i}_${i + 1}-b_no_audio_with_background.mp4`);
    console.log(` 7. add background image: ${outputVideoFileTemp1} and ${backgroundImage}`);
    if (i === 1) {
      const backgroundImageCover = path.join(__dirname, "bg_with_svg.jpeg");
      addBackgroundImage(backgroundImageCover, outputVideoFileTemp1, outputVideoFileTemp1WithBackground);
    } else {
      addBackgroundImage(backgroundImage, outputVideoFileTemp1, outputVideoFileTemp1WithBackground);
    }

    // 如果是第一个视频，则加入封面字幕
    if (i === 1) {
      console.log(` 6.1. add group info: ${outputVideoFileTemp1WithBackground}`);
      const assFileGroupInfo = path.join(tempDir, `${bookID}_${i}_${i + 1}-c_groupinfo.ass`);
      console.log(` 6.2. generate group info ass file: ${assFileGroupInfo}`);
      generateCoverASSFile(`《${bookGroup}》 Series`, bookTitle, assFileGroupInfo);
      // 合并视频和封面字幕
      const outputVideoFileWithGroupInfo = path.join(tempDir, `${bookID}_${i}_${i + 1}-d_with_groupinfo.mp4`);
      mergeAssVideo(outputVideoFileTemp1WithBackground, assFileGroupInfo, outputVideoFileWithGroupInfo);
      outputVideoFileTemp1WithBackground = outputVideoFileWithGroupInfo;
    }

    // 合并音频和视频
    const outputVideoFileTemp2 = path.join(tempDir, `${bookID}_${i}_${i + 1}-e_with_audio.mp4`);
    console.log(` 8. merge audio and video: ${outputVideoFileTemp1WithBackground} and ${audioFile}`);
    const targetDuration = getVideoDuration(outputVideoFileTemp1WithBackground);
    mergeAudioVideo(outputVideoFileTemp1WithBackground, audioFile, outputVideoFileTemp2, targetDuration);
    // 生成字幕文件
    const assFile = path.join(tempDir, `${bookID}_${i}_${i + 1}-f.ass`);
    console.log(` 9. generate ass file: ${assFile}`);
    generateASSFile(text, text_cn, audioDuration + 1, assFile);

    // 生成单词字幕
    const wordAssFilePath = path.join(tempDir, `${bookID}_${i}_${i + 1}-g_word.ass`);

    process.env.wordType = "nomoveeffect";
    await generateWordASSFromFile(assFile, wordAssFilePath, config.projectSettings.wordStyles);
    process.env.wordType = undefined;

    // 合并视频和字幕
    const outputVideoFile = path.join(tempDir, `${bookID}_${i}_${i + 1}-h_with_ass.mp4`);
    mergeAssVideo(outputVideoFileTemp2, assFile, outputVideoFile);

    if (i > 1) {
      // 合并视频和单词字幕
      const outputVideoFileWithWordAss = path.join(tempDir, `${bookID}_${i}_${i + 1}-i_with_word_ass.mp4`);
      console.log(` 10. merge word ass file: ${assFile}`);
      mergeAssVideo(outputVideoFile, wordAssFilePath, outputVideoFileWithWordAss);
      tempVideos.push(outputVideoFileWithWordAss);
    } else {
      tempVideos.push(outputVideoFile);
    }
  }
  resetVideosTime(tempVideos);
  function mergeVideosByList(opf) {
    // 创建list.txt文件
    const listFile = path.join(tempDir, "list.txt");
    // 遍历tempVideos，获取时长
    const listFileContent = tempVideos
      .map(videoFile => {
        const videoDuration = getVideoDuration(videoFile);
        return `file '${videoFile}'\nduration ${videoDuration}`;
      })
      .join("\n");
    fs.writeFileSync(listFile, listFileContent);
    process.env.FFLAGS = "true";
    mergeVideosOrderedByList(listFile, opf);
    process.env.FFLAGS = undefined;
  }
  // 合并所有临时视频
  const outputVideoFileNoEnding = path.join(tempDir, `${bookID}-no_ending.mp4`);
  mergeVideosByList(outputVideoFileNoEnding);

  tempVideos.length = 0;
  tempVideos.push(outputVideoFileNoEnding);
  const endingVideo = path.join(__dirname, "ending.mp4");
  // 将endingVideo拷贝到tempDir
  const targtendingVideo = path.join(tempDir, "ending.mp4");
  fs.copyFileSync(endingVideo, targtendingVideo);
  // 将targtendingVideo加入tempVideos中
  tempVideos.push(targtendingVideo);

  const outputVideoFile = path.join(bookDir, `${bookID}.mp4`);
  // 合并视频内容和结尾
  mergeVideosByList(outputVideoFile);
  // 清理tempDir下除了outputVideoFile的全部文件
  // cleanupTempDir();
}

/**
 * 重置视频时间
 * @param {Array<string>} videos
 */
function resetVideosTime(videos) {
  const encodeFormat = config.encodeFormat || "h264_nvenc";
  videos.map(v => {
    const tempVideoFile = path.join(tempDir, `${path.basename(v, ".mp4")}_reset.mp4`);
    const resetTimeCommand = `"${FFMPEG_PATH}" -y -loglevel error -i "${v}" -vf "setpts=PTS-STARTPTS" -c:v ${encodeFormat} -preset fast -threads 0 -crf 23 -c:a copy "${tempVideoFile}"`;
    execSync(resetTimeCommand, { stdio: "inherit" });
    // 将tempVideoFile重命名回v
    fs.renameSync(tempVideoFile, v);
  });
}

/**
 * 清理临时目录
 */
function cleanupTempDir() {
  const files = fs.readdirSync(tempDir);
  files.forEach(file => {
    fs.unlinkSync(path.join(tempDir, file));
  });
}

function getVideoDuration(video) {
  const durationCommand = `"${FFMPEG_PATH}" -i "${video}" 2>&1 | findstr "Duration"`;
  console.log(`  获取视频时长...,command : ${durationCommand}`);
  const durationOutput = execSync(durationCommand, { encoding: "utf-8" });
  console.log("  获取视频时长输出:", durationOutput);
  const durationMatch = durationOutput.match(/Duration: (\d{2}:\d{2}:\d{2}\.\d{2})/);
  let duration;
  if (durationMatch) {
    const timeParts = durationMatch[1].split(":");
    duration = parseFloat(timeParts[0]) * 3600 + parseFloat(timeParts[1]) * 60 + parseFloat(timeParts[2]);
  } else {
    throw new Error("  Could not extract duration from ffmpeg output");
  }
  return duration;
}
function generateASSFile(text, text_cn, duration, outputFile) {
  if (duration < 10) {
    duration = "0" + duration;
  }
  const textStyles = config.projectSettings?.textStyles || {};
  let assContent = `[Script Info]
Title: Default Aegisub file
ScriptType: v4.00+
WrapStyle: 0
ScaledBorderAndShadow: yes
YCbCr Matrix: TV.601
PlayResX: ${config.projectSettings.videoWidth}
PlayResY: ${config.projectSettings.videoHeight}

[V4+ Styles]
${generateASSStylesSingleLine(textStyles)}

[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:00.50,0:00:${duration.toString().padStart(2, "0")},Current,,0,0,0,,${text}
Dialogue: 0,0:00:00.50,0:00:${duration.toString().padStart(2, "0")},Current_cn,,0,0,0,,${text_cn}
`;
  // 保存ASS文件
  fs.writeFileSync(outputFile, assContent);
}

function generateCoverASSFile(groupName, bookName, outputFile) {
  let assContent = `[Script Info]
Title: Default Aegisub file
ScriptType: v4.00+
WrapStyle: 0
ScaledBorderAndShadow: yes
YCbCr Matrix: TV.601
PlayResX: ${config.projectSettings.videoWidth}
PlayResY: ${config.projectSettings.videoHeight}
  
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: BookGroup,DynaPuff,72,&HFF00FF&,&H000000FF,&H000000&,&H80000000,1,0,0,0,100,100,0,0,1,2,1,2,10,10,460,1
Style: BookTitle,DynaPuff,72,&H000000FF&,&H000000FF,&H33FFFF&,&H80000000,1,0,0,0,100,100,0,0,1,20,1,2,10,10,1460,1
Style: Line1,Roman,12,&HFF00FF&,&H666666FF,&H000000&,&H80000000&,0,0,0,0,100,100,0,0,1,2,1,2,10,10,420,1
Style: Line2,Roman,12,&HFF00FF&,&H000000FF,&H000000&,&H80000000&,0,0,0,0,100,100,0,0,1,2,1,2,10,10,420,1
[Events]
Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text
Dialogue: 0,0:00:00.00,0:00:03.00,BookGroup,,0,0,0,,${groupName}
Dialogue: 0,0:00:00.00,0:00:03.00,BookTitle,,0,0,0,,${bookName}
Dialogue: 0,0:00:00.00,0:00:03.00,Line1,,0,0,0,,————————————————————————————————————
Dialogue: 0,0:00:00.00,0:00:03.00,Line2,,0,0,0,,————————————————————————————————————
  `;
  // 保存ASS文件
  fs.writeFileSync(outputFile, assContent);
}

async function main() {
  // 在bookDir下创建temp 目录,如果不存在的话
  console.log(` 创建temp 目录: ${tempDir}`);
  if (!fs.existsSync(tempDir)) {
    fs.mkdirSync(tempDir);
  } else {
    //清理temp目录内容，但是不删除目录
    cleanupTempDir();
  }
  await drawSvg2BackgroundImage();
  await createBook("Friends", "1549");
}
main();
